package com.gupaoedu.springframework.mvcframework.context;

import com.gupaoedu.springframework.mvcframework.annotation.MyAutowired;
import com.gupaoedu.springframework.mvcframework.annotation.MyController;
import com.gupaoedu.springframework.mvcframework.annotation.MyService;
import com.gupaoedu.springframework.mvcframework.beans.MyBeanFactory;
import com.gupaoedu.springframework.mvcframework.beans.MyBeanWrapper;
import com.gupaoedu.springframework.mvcframework.beans.config.MyBeanDefinition;
import com.gupaoedu.springframework.mvcframework.beans.support.MyBeanDefinitionReader;
import com.gupaoedu.springframework.mvcframework.beans.support.MyDefaultListableBeanFactory;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;

/**
 * projectName: my-springframwork
 * packageName: com.gupaoedu.springframework.mvcframework.context
 * Created: 2019/4/11.
 * Auther: baifc
 * Description:
 */
public class MyApplicationContext extends MyDefaultListableBeanFactory implements MyBeanFactory {

    private String[] configLocations;

    private MyBeanDefinitionReader reader;

    private Map<String, Object> factoryBeanObjectCache = new HashMap<>();

    // IOC容器
    private Map<String, MyBeanWrapper> factoryBeanInstanceCache = new HashMap<>();

    public MyApplicationContext(String... configLocations) {
        this.configLocations = configLocations;
        try {
            refresh();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void refresh() throws Exception {
        // 定位配置文件
        reader = new MyBeanDefinitionReader(configLocations);

        // 加载配置文件, 扫描相关的类，把它们封装成BeanDefinition
        List<MyBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();

        // 注册，将配置文件信息放置于ioc容器中
        doRegisterBeanDefinition(beanDefinitions);

        // 把不是延时加载的类，提前初始化
        doAutowired();

    }

    private void doRegisterBeanDefinition(List<MyBeanDefinition> beanDefinitions) throws Exception {
        for (MyBeanDefinition beanDefinition : beanDefinitions) {
            if (super.beanDefinitionMap.containsKey(beanDefinition.getFactoryBeanName())) {
                throw new Exception("The “" + beanDefinition.getFactoryBeanName() + "” is exists!!");
            }
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    private void doAutowired() {
        for (Map.Entry<String, MyBeanDefinition> entry : super.beanDefinitionMap.entrySet()) {
            String beanName = entry.getKey();
            if (!entry.getValue().isLazyInit()) {
                getBean(beanName);
            }
        }
    }

    //依赖注入，从这里开始，通过读取BeanDefinition中的信息
    //然后，通过反射机制创建一个实例并返回
    //Spring做法是，不会把最原始的对象放出去，会用一个BeanWrapper来进行一次包装
    public Object getBean(String beanName) {
        MyBeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        //1、初始化
        Object instance = instantiateBean(beanName, beanDefinition);

        MyBeanWrapper beanWrapper = new MyBeanWrapper(instance);

        factoryBeanInstanceCache.put(beanName, beanWrapper);

        //2、注入
        populateBean(beanName, new MyBeanDefinition(), beanWrapper);

        return factoryBeanInstanceCache.get(beanName).getWrapperInstance();
    }

    private Object instantiateBean(String beanName, MyBeanDefinition beanDefinition) {

        String beanClassName = beanDefinition.getBeanClassName();

        Object instance = null;
        try {
            if (factoryBeanObjectCache.containsKey(beanClassName)) {
                return factoryBeanObjectCache.get(beanClassName);
            } else {
                Class<?> beanClass = Class.forName(beanDefinition.getBeanClassName());

                instance = beanClass.newInstance();

                // TODO: 调试
                factoryBeanObjectCache.put(beanClassName, instance);
                factoryBeanObjectCache.put(beanDefinition.getFactoryBeanName(), instance);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
        return instance;

    }

    // 注入
    private void populateBean(String beanName, MyBeanDefinition myBeanDefinition, MyBeanWrapper beanWrapper) {
        Class<?> clazz = beanWrapper.getWrapperClass();
        Object instance = beanWrapper.getWrapperInstance();

        if (!clazz.isAnnotationPresent(MyService.class) && !clazz.isAnnotationPresent(MyController.class)) {
            return;
        }

        Field [] fields = clazz.getDeclaredFields();

        if (fields.length == 0) {
            return;
        }

        for (Field field : fields) {
            if (!field.isAnnotationPresent(MyAutowired.class)) {
                continue;
            }
            MyAutowired myAutowired = field.getAnnotation(MyAutowired.class);
            String autowiredValue = myAutowired.value();
            if ("".equals(autowiredValue)) {
                autowiredValue = field.getType().getName();
            }

            field.setAccessible(true);

            try {
                if(this.factoryBeanInstanceCache.get(autowiredValue) == null){ continue; }

                field.set(instance, factoryBeanInstanceCache.get(autowiredValue).getWrapperInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }

        }

    }

    public String [] getBeanDefinitionNames() {
        return beanDefinitionMap.keySet().toArray(new  String[this.beanDefinitionMap.size()]);
    }

//    private void populateBean(String beanName, GPBeanDefinition gpBeanDefinition, GPBeanWrapper gpBeanWrapper) {
//    }
//
//    private void instantiateBean(String beanName, GPBeanDefinition gpBeanDefinition) {
//    }

    public Properties getConfig() {
        return reader.getProperties();
    }
}
